
#ifndef _CACHE_H__INCLUDED_
#define _CACHE_H__INCLUDED_

#include <memory>
#include "Utility/AutoLock.h"
#include "Utility/RefPtr.h"
#include "Utility/TemplateImpl/CacheImpl.h"

namespace nebula { namespace utility {

	template<
		class KeyType,
		class ValueType,
		class ThreadMode = single_thread,
			template<class _k, class _v> class CacheTemplate = lru_cache
	>
	class cache;

	template<
		class KeyType,
		class ValueType,
			template<class _k, class _v> class CacheTemplate
	>
	class cache<KeyType, ValueType, single_thread, CacheTemplate>
	{
	public:
		typedef KeyType													key_type;
		typedef ValueType												value_type;
		typedef single_thread											thread_mode;
		typedef CacheTemplate<key_type, value_type>						cache_type;
		typedef cache<key_type, value_type, thread_mode, CacheTemplate> this_type;

	private:
		typedef ref_ptr<cache_type, single_thread> _impl_holder;

	public:
		cache(unsigned size) :
		  _impl(new cache_type(size))
		  {
		  }

		  cache(const this_type& rhs) :
		  _impl(rhs._impl)
		  {
		  }

		  this_type& operator=(const this_type& rhs)
		  {
			  _impl = rhs._impl;
		  }

	public:

		//get value by correspondent key, and the spec key-value pair 
		// will be marked as newest one
		bool get(const key_type &key, value_type& val)
		{
			return _impl->get(key, val);
		}

		//set value by correspondent key, and the spec key-value pair 
		// will be marked as newest one
		//Note: if the cache is full, the oldest key-value pair will be removed
		void set(const key_type &key, const value_type& val)
		{
			return _impl->set(key, val);
		}

		//pop the newest key-value pair from cache
		bool pop(value_type& val)
		{
			return _impl->pop(val);
		}

		//pull the oldest key-value pair from cache
		bool pull(value_type& val)
		{
			return _impl->pull(val);
		}

		//remove key-value pair by correspondent key
		bool remove(const key_type &key)
		{
			return _impl->remove(key);
		}

		//clear the whole cache
		void clear()
		{
			value_type val;
			while (_impl->pop(val)) ;
		}

		//check if the cache is full
		bool is_full()
		{
			return _impl->is_full();
		}

	private:
		_impl_holder _impl;
	};


	template<
		class KeyType,
		class ValueType,
			template<class _k, class _v> class CacheTemplate
	>
	class cache<KeyType, ValueType, multi_thread, CacheTemplate>
	{
	public:
		typedef KeyType													key_type;
		typedef ValueType												value_type;
		typedef multi_thread											thread_mode;
		typedef CacheTemplate<key_type, value_type>						cache_type;
		typedef cache<key_type, value_type, thread_mode, CacheTemplate> this_type;
		typedef	cache<KeyType, ValueType, single_thread, CacheTemplate>	impl_type;

	public:
		cache(unsigned size) :
		  _impl(size),
			  _lock(new Mutex)
		  {
		  }

		  cache(const this_type& rhs) :
		  _impl(rhs._impl), _lock(rhs._lock)
		  {
		  }

		  this_type& operator=(const this_type& rhs)
		  {
			  _impl = rhs._impl;
			  _lock = rhs._lock;
		  }

	public:

		bool get(const key_type &key, value_type& val)
		{
			AutoLock lock(_lock.get());
			return _impl.get(key, val);
		}

		void set(const key_type &key, const value_type& val)
		{
			AutoLock lock(_lock.get());
			_impl.set(key, val);
		}

		bool pop(value_type& val)
		{
			AutoLock lock(_lock.get());
			return _impl.pop(val);
		}

		bool pull(value_type& val)
		{
			AutoLock lock(_lock.get());
			return _impl->pull(val);
		}

		bool remove(const key_type &key)
		{
			AutoLock lock(_lock.get());
			return _impl.remove(key);
		}

		void clear()
		{
			AutoLock lock(_lock.get());
			value_type val;
			while (_impl->pop(val)) ;
		}

		bool is_full()
		{
			AutoLock lock(_lock.get());
			return _impl->is_full();
		}

	private:
		impl_type					_impl;
		ref_ptr<Mutex, thread_mode> _lock;
	};

}} // namespace nebula::utility

#endif
